/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: qnx_render.cpp
 *
 * Purpose: implementation a render
 *
 * Developer:
 *   wen.gu , 2022-03-04
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#if defined(HAS_QNX_SCREEN) && HAS_QNX_SCREEN
#include "qnx_render.h"

#include <chrono>

/******************************************************************************
 **    MACROS
 ******************************************************************************/
#define LOGD printf
#define LOGE printf
/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

/******************************************************************************
 **    inner FUNCTION DEFINITIONS
 ******************************************************************************/
static bool init_render(screen_context_t& screen_ctx_, screen_window_t& screen_win_, const player_param& param)
{
    int display_id_ = param.display_id;
    int window_zorder_ = param.zorder;
    int img_width = param.img_width;
    int img_height = param.img_height;
   // int buffer_count = 2;
    int32_t ivs[] = 
    {
        SCREEN_PROPERTY_VISIBLE, 1, 1, /** default visible is true(1) */
        SCREEN_PROPERTY_USAGE, 1, SCREEN_USAGE_WRITE|SCREEN_USAGE_NATIVE,
        SCREEN_PROPERTY_FORMAT, 1, SCREEN_FORMAT_NV12,
        SCREEN_PROPERTY_BUFFER_SIZE, 2, img_width, img_height, /** buffer width, height */
        SCREEN_PROPERTY_ZORDER, 1, window_zorder_,
        SCREEN_PROPERTY_SOURCE_POSITION, 2,0,0,
        SCREEN_PROPERTY_SOURCE_SIZE, 2, img_width, img_height, /** buffer width, height */
        /*SCREEN_PROPERTY_DEBUG, 1, SCREEN_DEBUG_STATISTICS,*/
        WIN_IV_END
    };
    LOGD("[%s.%d]===>: zorder: %d, width: %d, height: %d\n", __FUNCTION__, __LINE__, window_zorder_, img_width, img_height);
    int rc = screen_create_context(&screen_ctx_, SCREEN_APPLICATION_CONTEXT);

    if (rc != 0)
    {
        LOGE("create screen context failed(%s)\n", strerror(errno));
        return false;
    }
    LOGD("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    rc = CreateWindow(screen_ctx_, screen_win_, display_id_, ivs); 

    LOGD("[%s.%d]===>:\n", __FUNCTION__, __LINE__);    
    if (rc == 0)
    {
        int buffer_count = 2;
        LOGD("[%s.%d]===>: buffer count: %d\n", __FUNCTION__, __LINE__, buffer_count);
        rc = screen_create_window_buffers(screen_win_, buffer_count);  
        if (rc != 0)
        {
            LOGE("alloc window buffer failed(%s), buffer count: %d\n", strerror(errno), buffer_count);
            return false;
        }

        LOGD("[%s.%d]===>:\n", __FUNCTION__, __LINE__);

        return true;
    }    
    else
    {
        LOGE("create window failed(%s)\n", strerror(errno));
    }

    return false;    
}

static uint8_t* get_render_buffer(screen_window_t screen_win_, screen_buffer_t& screen_buf, int32_t& uv_pos)
{
    int buf_count = 0;

    screen_get_window_property_iv(screen_win_, SCREEN_PROPERTY_BUFFER_COUNT, (int *)&buf_count);

    screen_buffer_t render_buf[buf_count];
    screen_get_window_property_pv(screen_win_, SCREEN_PROPERTY_RENDER_BUFFERS, (void **)&render_buf);

    screen_buf = render_buf[0];    

    if (screen_buf)
    {
        uint8_t* buf_ptr = nullptr;
        //int stride =0;
        screen_get_buffer_property_pv(screen_buf, SCREEN_PROPERTY_POINTER, (void **)&buf_ptr); 
        //do_print = true;
        int32_t stride = 0;
        int32_t buf_size[2] = {0};
        int32_t frame_size = 0;
        int32_t planer_offsets[3] = {0};
        screen_get_buffer_property_iv(screen_buf, SCREEN_PROPERTY_STRIDE, &stride);
        screen_get_buffer_property_iv(screen_buf, SCREEN_PROPERTY_BUFFER_SIZE, buf_size);
        screen_get_buffer_property_iv(screen_buf, SCREEN_PROPERTY_SIZE, &frame_size);
        screen_get_buffer_property_iv(screen_buf, SCREEN_PROPERTY_PLANAR_OFFSETS, planer_offsets);
#if 0        
        LOGE("====>: screen buffer stride: %d, buffer size: wxh(%d x %d), frame_size: %d, planer offset: %d, %d, %d\n", 
            stride, buf_size[0], buf_size[1], frame_size, planer_offsets[0], planer_offsets[1], planer_offsets[2]);
#endif            
        uv_pos = planer_offsets[1];
        return buf_ptr;
    }

    return nullptr;
}
/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/

QnxRender::~QnxRender()
{
    if (screen_win_)
    {
        screen_destroy_window(screen_win_);
    }

    if (screen_ctx_)
    {
        screen_destroy_context(screen_ctx_);
    }
}

bool QnxRender::initialize(const player_param& param)
{
    img_width_ = param.img_width;
    img_height_ = param.img_height;
    return init_render(screen_ctx_, screen_win_, param);
}

bool QnxRender::getRenderBuffer(RenderBuffer& buffer)
{
    screen_buffer_t screen_buf = nullptr;
    int32_t uv_pos = 0;
    uint8_t* dst_buf = get_render_buffer(screen_win_, screen_buf, uv_pos);

    if (dst_buf)
    {
        buffer.buf_y = dst_buf;
        buffer.buf_uv = dst_buf + uv_pos;
        buffer.opaque = (void*)screen_buf;
        return true;
    }

    return false;
}

bool QnxRender::render(const KfBuffer& buffer)
{
    if (!screen_win_ || !buffer.buf)
    {
        return false;
    }
    RenderBuffer render_buffer;
    if (getRenderBuffer(render_buffer) == false)
    {
        return false;
    }

    int32_t src_pos = img_width_ * img_height_;
    int32_t uv_size = src_pos / 2;

    memcpy(render_buffer.buf_y, out_buf.buf, src_pos);
    //LOGD("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    memcpy(render_buffer.buf_uv, out_buf.buf + src_pos, uv_size);   


    int rect[4] = {0, 0, img_width_, img_height_};
    //LOGD("[%s.%d]===>:\n", __FUNCTION__, __LINE__);
    return screen_post_window(screen_win_, screen_buffer_t(buffer.opaque), 1, rect, 0) == 0; //SCREEN_WAIT_IDLE
}

#endif /** end of HAS_QNX_SCREEN */